﻿/*
	Copyright 2009 Jason Nottingham

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.


    How to submit a patch:
    1. create or modify a test case (under 'tests') so that it fails to compile/link/run without 
       your change
    2. verify you have the latest sources (resync/merge)
    3. fix or add the missing functionality
    4. verify that your changes fixes the newly broken test case in #1 - it now builds and runs 
       C++ the same as the original C#
    5. create a Subversion-friendly diff or patch file of your changes (including the test cases)
    6. submit your patch at http://gonative.codeplex.com


*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;


namespace GoNative
{

    #region methods

    /// <summary>
    /// 
    /// </summary>
    internal class Method
    {
        private string body;
        private readonly string declaration;
        private readonly string definition;
        private readonly bool isPublic;
        private readonly bool isStatic;
        private readonly bool isEnum;
        private readonly string summaryComment;
        private readonly string sha1;

        private string[] enumValues;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="className"></param>
        /// <param name="namespaceName"></param>
        /// <param name="sourceFile"></param>
        /// <param name="enums"></param>
        public Method(string source, string className, string namespaceName, string sourceFile, Dictionary<string,string> enums)
        {
            Globals.PushScopedVariables(source);
            sha1 = Globals.CalculateSha1ForCode(source);

            // there are some cases where we need to convert unmanaged types to 'managed' types, so
            // do that before we take a snap-shot of the variables at this scope level
            source = Globals.ConvertVarToCorrectVariableType(source);
            source = Globals.ConvertUnmanagedVariablesToManagedAsNeeded(source);

            int i = source.IndexOf("{");
            if ( i == -1 )
                return;
            declaration = Globals.RemoveGunk(source.Substring(0, i - 1));

            isPublic = false;
            if (declaration.IndexOf("public ") > -1)
                isPublic = true;
            if (declaration.StartsWith("static void Main("))
                isPublic = true;

            isStatic = declaration.Contains("static ");

            declaration = declaration.Replace("( )", "()");
            declaration = declaration.Replace("protected ", "").Replace("override ", "");
            declaration = declaration.Replace("public ", "").Replace("private ", "").Trim();

            if (declaration.Contains("[] "))
                declaration = Globals.ConvertArraysToLists(declaration);

            // find the definition by finding the '{' and working backwards, but ignore
            // the '{'s that appear in comments
            int start = Globals.StringBefore(declaration, "(").LastIndexOf(" ") + 1;
            
            definition = string.IsNullOrEmpty(namespaceName) ? 
                string.Format("{0}{1}::{2}", declaration.Substring(0, start), className, declaration.Substring(start)) :
                string.Format("{0}{1}::{2}::{3}", declaration.Substring(0, start), namespaceName, className, declaration.Substring(start));

            definition = definition.Replace("static ", "");
            isEnum = !string.IsNullOrEmpty(definition) && definition.StartsWith("enum ");

            if (isEnum)
            {
                RecordEnumValues(source);
                foreach (string enumValue in enumValues)
                    enums[enumValue] = enumValue;
            }

            if (SubstitutingSourceCode(source,sourceFile))
                return;

            summaryComment = "// BUGBUG - need to capture comments here\n// GoNative Replacement Hash: " + sha1;

            string sourceToParse = source.Substring(i);

            if (sourceToParse.Contains("DateTime.Now"))
            {
                sourceToParse = sourceToParse.Replace("DateTime.Now", "DateTime.Now()");
                sourceToParse = Globals.ReplaceCompletely(sourceToParse, "DateTime.Now()()", "DateTime.Now()");
            }

            // remove try/catch/finally (they will have one or more tabs in front of them)
            sourceToParse = sourceToParse.Replace("\ttry", "\t//try");
            sourceToParse = sourceToParse.Replace("\tcatch", "\n// BUGBUG - GoNative not currently support try catch, try rewriting\n" +
                "#pragma message( \"GONATIVE -- removed 'catch'\") \n\tif ( 0 ) // was: catch");
            sourceToParse = sourceToParse.Replace("\tfinally", "\t//finally");

            string[] bodyLines = sourceToParse.Split('\n');

            // remove most of the indenting
            for (i = 0; i < bodyLines.Length; i++)
            {
                string s = bodyLines[i].Replace("\t\t\t", "\t");
                s = s.Replace("\t\t}", "}").Replace("\r", "");
                bodyLines[i] = s;
            }

            // now convert the body from C# to C/C++
            StringBuilder sbBody = new StringBuilder();
            int sourceIndex = 0;
            while (sourceIndex < bodyLines.Length)
            {
                string s = bodyLines[sourceIndex];

                if (sourceIndex < bodyLines.Length)
                    s = Globals.ProcessLine(bodyLines, ref sourceIndex, enums);

                if ( !string.IsNullOrEmpty(s))
                    sbBody.Append(s + "\n");
                sourceIndex++;
            }
            body = sbBody.ToString();

            // comment out the body if it is a form Dispose() method
            if (className.Contains("Form") && declaration.Contains("void Dispose("))
            {
                i = body.IndexOf("{");
                if (i != -1)
                    body = body.Insert(i + 2, "/*\n");
                i = body.LastIndexOf("}");
                if (i != -1)
                    body = body.Insert(i, "\n\t*/\n");
            }

        }

        private void RecordEnumValues(string source)
        {
            int start = source.IndexOf("{");
            int end = source.IndexOf("}", start);
            string sub = source.Substring(start + 1, end - start - 1);
            sub = sub.Replace("\t", "").Replace("\n", "").Replace("\r", "");
            enumValues = sub.Split(',');
            string enumName = declaration.Split(' ')[1];
            for (int k = 0; k < enumValues.Length; k++)
                enumValues[k] = string.Format("{0}.{1}", enumName, enumValues[k]);
        }

        private bool SubstitutingSourceCode(string source, string sourceFile)
        {
            string relativePath = Globals.ReadFieldValue(source, "GoNative Substitute Source:");
            if ( string.IsNullOrEmpty(relativePath))
                return false;
            FileInfo fi = new FileInfo(sourceFile);
            string sourceDirectory = fi.DirectoryName;
            if (string.IsNullOrEmpty(sourceDirectory))
                sourceDirectory = ".";
            string fullPath = Path.Combine(sourceDirectory, relativePath);
            body = string.Format("// this code was substituted from {0}\r\n{1}", 
                        relativePath,
                        File.ReadAllText(fullPath).Replace(definition + "\r\n", ""));

            // compare the hash in the buffer just read in, warn if it differs
            string sha1Replacement = Globals.ReadFieldValue(body, "GoNative Replacement Hash:");

            if ( sha1 != sha1Replacement)
                Console.WriteLine("The SHA1 hash stored with the substituted code does not match.  The original C# code may have changed, invalidating the substituted code.");

            return true;
        }

        public string Declaration
        {
            get { return declaration; }
        }

        public bool IsPublic
        {
            get { return isPublic; }
        }

        public bool IsStatic
        {
            get { return isStatic; }
        }

        public string Body
        {
            get
            {
                string s = string.Format("{0}\r\n{1}\r\n", definition, body);
                if (string.IsNullOrEmpty(summaryComment))
                    return s;
                return string.Format("{0}\r\n{1}", summaryComment, s);
            }
        }

        public bool IsEnum
        {
            get
            {
                return isEnum;
            }
        }

        public void WriteEnum(TextWriter writer)
        {
            StringBuilder sb = new StringBuilder("\n");
            string[] lines = body.Split('\n');
            string indent = "\t\t\t";

            string s = definition;
            if (s.StartsWith("enum "))
            {
                int lastColon = s.LastIndexOf(":");
                s = s.Remove(5, lastColon - 5 + 1);
            }
            sb.AppendFormat("{0}{1}", indent, s);
            for (int i = 0; i < lines.Length; i++)
                sb.AppendFormat("\n{0}{1}", indent, lines[i]);
            sb.Append(";");
            writer.WriteLine(sb.ToString());
        }
    }

    #endregion

    #region classes
    /// <summary>
    /// 
    /// </summary>
    internal class Class
    {
        private readonly string nameWithInheritence;
        private readonly string nameSpace;
        private readonly List<Method> privateMethods;
        private readonly List<string> privateVariables;
        private readonly List<Method> publicMethods;
        private readonly List<string> publicVariables;
        private readonly Dictionary<string,string> enums;

        private readonly string name;
        public string Name
        {
            get { return name; }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="nameSpaceName"></param>
        /// <param name="sourceFile"></param>
        public Class(string source, string nameSpaceName, string sourceFile)
        {
            privateVariables = new List<string>();
            publicVariables = new List<string>();
            privateMethods = new List<Method>();
            publicMethods = new List<Method>();

            enums = new Dictionary<string, string>();

            nameSpace = nameSpaceName;
            Globals.PushScopedVariables(source);
            source = Globals.RemoveGlobalDirectives(source);
            if ( string.IsNullOrEmpty(source))
                return;

            // find the name by finding the '{' and working backwards, but ignore
            // the '{'s that appear in comments
            nameWithInheritence = Globals.RemoveGunk(source);
            int start = nameWithInheritence.IndexOf(" ") + 1;
            int end = nameWithInheritence.IndexOf("{") - 1;
            nameWithInheritence = nameWithInheritence.Substring(start, end - start);
            start = nameWithInheritence.IndexOf("class");
            if ( start > -1)
            {
                // probably a partial class
                nameWithInheritence = nameWithInheritence.Substring(start + 5).Trim();
            }
            start = nameWithInheritence.IndexOf(":");
            if (start > -1)
                nameWithInheritence = nameWithInheritence.Insert(start + 1, " public");

            name = nameWithInheritence.Split(':')[0].Trim();

            const string firstBracketOfClassString = "\n\t{";
            const string endOfMethodString = "\n\t\t}";
            int endOfNextMethod = source.IndexOf(endOfMethodString);

            int startOfNextMethod = source.IndexOf(firstBracketOfClassString) + firstBracketOfClassString.Length + 1;

            while (endOfNextMethod > -1)
            {
                int i = startOfNextMethod;
                endOfNextMethod += endOfMethodString.Length;
                startOfNextMethod = FindTrueStartOfNextMethod(source, startOfNextMethod);

                if (startOfNextMethod == -1)
                    startOfNextMethod = endOfNextMethod;

                if (i != startOfNextMethod)
                    ProcessClassLevelVariables(source.Substring(i, startOfNextMethod - i + 1));

                Method method = new Method( 
                    source.Substring(startOfNextMethod, endOfNextMethod - startOfNextMethod + 1),
                    name, nameSpaceName, sourceFile, enums);
                if (method.IsPublic)
                    publicMethods.Add(method);
                else
                    privateMethods.Add(method);
                startOfNextMethod = endOfNextMethod + 1;
                endOfNextMethod = source.IndexOf("\n\t\t}", startOfNextMethod);
            }
            // startOfNextMethod is now just past the block we have just processed
            ProcessClassLevelVariables(source.Substring(startOfNextMethod));

            privateVariables.Sort();
            publicVariables.Sort();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        private void ProcessClassLevelVariables(string source)
        {
            string[] lines = source.Trim().Split(';');
            for (int i = 0; i < lines.Length; i++)
            {
                string s = Globals.RemoveGunk(lines[i]).Replace("readonly ","");
                if (!s.StartsWith("[DllImport") && !string.IsNullOrEmpty(s))
                {
                    string[] vars = s.Split(',');
                    if (vars.Length > 1)
                    {
                        int k = vars[0].LastIndexOf(" ");
                        string declare = (k == -1) ? vars[0] : vars[0].Substring(0, k);
                        for (int j = 1; j < vars.Length; j++)
                            vars[j] = Globals.RemoveGunk(declare + " " + vars[j]);
                    }

                    if (s.Contains("private "))
                    {
                        foreach (string var in vars)
                        {
                            string variable = ScrubVariableName(var);
                            privateVariables.Add(variable + ";");
                            if (name.Contains("Form"))
                                AddControlId(variable);
                        }
                    }
                    else if (s.Contains("public "))
                    {
                        foreach (string var in vars)
                        {
                            string variable = ScrubVariableName(var);
                            publicVariables.Add(variable + ";");
                            if (name.Contains("Form"))
                                AddControlId(variable);
                        }
                    }
                }
            }
        }

        private void AddControlId(string variable)
        {
            string[] fields = variable.Split(' ');
            string varName = fields[fields.Length - 1];
            string last = name.ToUpper() + "_" + varName.ToUpper() + "_";
            if ( variable.Contains("Timer "))
                enums[varName + ".dwTickID"] = "ID_" + last + "TICK";
            else if ( variable.Contains("Button "))
                enums[varName + ".dwControlID"] = "ID_" + last + "CLICK";
        }

        private static string ScrubVariableName(string variable)
        {
            variable = variable.Replace("private ", "").Replace("public ", "");
            int i = variable.IndexOf("=");
            if (i > -1)
                variable = variable.Substring(0,i);
            return variable.Trim();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        private static int FindTrueStartOfNextMethod(string source, int start)
        {
            // find the starting bracket of the next method
            int firstBracket = source.IndexOf("\n\t\t{", start);
            if (firstBracket == -1)
                return -1;

            // then back-up to the start of it's Declaration
            // ...
            // then back-up to the start of any preceeding comments

            string beforeBracket = source.Substring(start, firstBracket - start);
            // let's be really simple for now and take the first blank line
            int methodStart = beforeBracket.LastIndexOf("\n\r\n");
            if (methodStart == -1)
                methodStart = beforeBracket.LastIndexOf("\n\t\t\r\n");
            if (methodStart == -1)
                methodStart = beforeBracket.LastIndexOf("\n\t\t");
            return (methodStart == -1) ? start : methodStart + start;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceFile"></param>
        /// <param name="headerFile"></param>
        /// <param name="windowsApp"></param>
        public void WriteToFiles(TextWriter sourceFile, TextWriter headerFile, bool windowsApp)
        {
            headerFile.WriteLine("\tclass {0}\r\n\t{{", nameWithInheritence);

            headerFile.WriteLine("\t\tpublic:");

            WriteGeneratedEnums(headerFile);

            foreach (Method method in publicMethods)
                if (method.IsEnum)
                    method.WriteEnum(headerFile);

            if ( windowsApp && name.StartsWith("Form"))
            {
                headerFile.WriteLine("\t\t\tvoid OnCreate( HWND hWnd );");
                headerFile.WriteLine("\t\t\tvoid OnSize();");
                headerFile.WriteLine("\t\t\tvoid OnTimer( WPARAM wParam, LPARAM lParam );");
                headerFile.WriteLine("\t\t\tvoid OnCommand( WPARAM wParam, LPARAM lParam );");
                headerFile.WriteLine("\t\t\tvoid OnActivate();");
                headerFile.WriteLine("\t\t\tvoid OnPaint();");
                headerFile.WriteLine("\t\t\tvoid OnDestroy();");
            }

            foreach (string s in publicVariables)
                headerFile.WriteLine("\t\t\t" + s);
            foreach (Method method in publicMethods)
                if ( !method.IsEnum )
                    headerFile.WriteLine("\t\t\t" + method.Declaration + ";");

            headerFile.WriteLine("\t\tprivate:");
            foreach (Method method in privateMethods)
                if (method.IsEnum)
                    method.WriteEnum(headerFile);
            foreach (string s in privateVariables)
                headerFile.WriteLine("\t\t\t" + s);
            foreach (Method method in privateMethods)
                if (!method.IsEnum)
                    headerFile.WriteLine("\t\t\t" + method.Declaration + ";");
            headerFile.WriteLine("\t};\r\n");

            WriteStaticClassVariables(sourceFile, privateVariables);
            WriteStaticClassVariables(sourceFile, publicVariables);
            sourceFile.WriteLine();

            foreach (Method method in privateMethods)
                if (!method.IsEnum)
                {
                    if ( method.Declaration.Contains("InitializeComponent") && (enums.Count > 0) )
                    {
                        StringBuilder sb = new StringBuilder();
                        foreach (KeyValuePair<string, string> e in enums)
                            if (e.Key != e.Value)
                                sb.Append("\t" + e.Key + " = " + e.Value + ";\n");
                        string body = method.Body;
                        int start = body.IndexOf("{\n");
                        if ( start > -1 )
                        {
                            start += 2;
                            body = body.Insert(start, sb.ToString());
                        }
                        sourceFile.WriteLine(body);
                    }
                    else
                        sourceFile.WriteLine(method.Body);
                }
            foreach (Method method in publicMethods)
                if (!method.IsEnum)
                    sourceFile.WriteLine(method.Body);
        }

        // this write enums that were generated to implement control IDs
        // to the header file
        private void WriteGeneratedEnums(TextWriter headerFile)
        {
            int generatedEnums = 0;
            foreach (KeyValuePair<string, string> kvp in enums)
                if (kvp.Value != kvp.Key)
                    generatedEnums++;
            if (generatedEnums == 0)
                return;

            int i = 0;
            int last = enums.Count - 1;
            headerFile.WriteLine("\t\t\tenum\n\t\t\t{");

            foreach (KeyValuePair<string, string> kvp in enums)
            {
                headerFile.Write("\t\t\t\t" + kvp.Value);
                if (i == 0)
                    headerFile.Write(" = 1");
                if (i < last)
                    headerFile.WriteLine(",");
                else
                    headerFile.WriteLine();
                i++;
            }
            headerFile.WriteLine("\t\t\t};");
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceFile"></param>
        /// <param name="variables"></param>
        private void WriteStaticClassVariables(TextWriter sourceFile, IEnumerable<string> variables)
        {
            string header = nameSpace + "::" + name + "::";
            foreach (string s in variables)
                if ( s.Contains("static "))
                {
                    string toWrite = s.Replace("static ", "").Trim();
                    int lastSpace = toWrite.LastIndexOf(" ");
                    if ( lastSpace > -1 )
                    {
                        lastSpace++;
                        toWrite = toWrite.Insert(lastSpace, header);
                        sourceFile.WriteLine(toWrite);
                    }
                }
        }

        public void MergePartialClasses(Class other)
        {
            foreach (var list in other.publicVariables)
                publicVariables.Add(list);
            foreach (var list in other.privateVariables)
                privateVariables.Add(list);
            foreach (var list in other.publicMethods)
                publicMethods.Add(list);
            foreach (var list in other.privateMethods)
                privateMethods.Add(list);
            foreach (var e in other.enums)
                enums[e.Key] = e.Value;
        }
    }

    #endregion

    #region namespaces

    /// <summary>
    /// 
    /// </summary>
    internal class NameSpace
    {
        private readonly Dictionary<string, Class> classes;
        private readonly string nameSpaceName;

        public string Name { get { return nameSpaceName; } }
        public Class[] Classes
        {
            get
            {
                Class[] c = new Class[classes.Count];
                int i = 0;
                foreach (KeyValuePair<string, Class> de in classes)
                    c[i++] = de.Value;
                return c;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="sourceFile"></param>
        public NameSpace(string source, string sourceFile)
        {
            classes = new Dictionary<string, Class>();
            Globals.PushScopedVariables(source);

            int startOfNameSpace = source.IndexOf("{");
            int indent = "namespace ".Length;
            nameSpaceName = Globals.RemoveGunk(source.Substring(indent, startOfNameSpace - indent - 1));

            int startOfNextClass = startOfNameSpace + 1;
            int endOfNextClass = source.IndexOf("\n\t}");
            while (endOfNextClass > -1)
            {
                Class newClass = new Class(source.Substring(startOfNextClass, endOfNextClass - startOfNextClass + 1),
                                           nameSpaceName, sourceFile);

                if (classes.ContainsKey(newClass.Name))
                    classes[newClass.Name].MergePartialClasses(newClass);
                else
                    classes[newClass.Name] = newClass;

                startOfNextClass = endOfNextClass + 1;
                endOfNextClass = source.IndexOf("\n\t}", startOfNextClass);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string ConsoleAppNameSpace
        {
            get
            {
                foreach (KeyValuePair<string, Class> kvp in classes)
                {
                    if (kvp.Value.Name == "Program")
                        return nameSpaceName;
                }
                return string.Empty;
            }
        }

        public string FunctionHeader
        {
            get
            {
                foreach (KeyValuePair<string, Class> kvp in classes)
                {
                    // just use the first one
                    return string.Format("{0}::{1}::", Name, kvp.Value.Name);
                }
                return string.Empty;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceFile"></param>
        /// <param name="headerFile"></param>
        /// <param name="windowsApp"></param>
        public void WriteToFiles(TextWriter sourceFile, TextWriter headerFile, bool windowsApp)
        {
            string nameSpaceHeader = string.Format("namespace {0}\n{{", nameSpaceName);
            headerFile.WriteLine(nameSpaceHeader);
            foreach( KeyValuePair<string,Class> kvp in classes )
                kvp.Value.WriteToFiles(sourceFile, headerFile, windowsApp);
            headerFile.WriteLine("}");
        }

        public void Merge(NameSpace space)
        {
            // merge classes from 'space' into this namespace
            foreach( KeyValuePair<string,Class> kvp in space.classes)
            {
                if (classes.ContainsKey(kvp.Key))
                    classes[kvp.Key].MergePartialClasses(kvp.Value);
                else
                    classes[kvp.Key] = kvp.Value;
            }
            // BUGBUG - merge variables? (none at this level?)
        }
    }

    #endregion

    #region sourcefiles

    /// <summary>
    /// 
    /// </summary>
    internal class SourceFile
    {
        public readonly string HeaderFileName;
        private readonly string inFileName;
        private readonly Dictionary<string,NameSpace> namespaces;
        private readonly string outFileName;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        public SourceFile(string fileName)
        {
            inFileName = fileName;
            outFileName = inFileName.Replace(".cs", ".cpp");
            HeaderFileName = inFileName.Replace(".cs", ".h");
            namespaces = new Dictionary<string, NameSpace>();
        }

        /// <summary>
        /// 
        /// </summary>
        internal void Process(bool windowsApp, string headers)
        {
            Console.WriteLine("    Processing source file {0}", inFileName);
            string source = File.ReadAllText(inFileName).Replace("    ", "\t");

            if (windowsApp)
                source = source.Replace("object sender, EventArgs e", "");

            string designerFile = Globals.GetDesignerFileName(inFileName);
            if (File.Exists(designerFile))
                source += File.ReadAllText(designerFile).Replace("    ", "\t");

            source = Globals.HandlePreprocessorDirectives(source);

            int nextNameSpace = source.IndexOf("\nnamespace ");
            if (nextNameSpace == -1)
                nextNameSpace = source.IndexOf("namespace ");
            ProcessFileHeader(nextNameSpace > 0 ? source.Substring(0, nextNameSpace - 1) : source);
            while (nextNameSpace > -1)
            {
                int end = source.IndexOf("\n}", nextNameSpace);
                Debug.Assert(end != -1);
                if (end == -1)
                    break;

                NameSpace ns = new NameSpace(source.Substring(nextNameSpace, end - nextNameSpace + 1), inFileName );
                if (namespaces.ContainsKey(ns.Name))
                    namespaces[ns.Name].Merge(ns);
                else
                    namespaces[ns.Name] = ns;

                nextNameSpace = source.IndexOf("\nnamespace ", end);
            }

            WriteToFiles(windowsApp, headers);
        }

        /// <summary>
        /// 
        /// </summary>
        private void WriteToFiles(bool windowsApp, string headers)
        {
            string tempSourceFile = Path.GetTempFileName();
            string tempHeaderFile = Path.GetTempFileName();
            TextWriter sourceFile = new StreamWriter(tempSourceFile);
            TextWriter headerFile = new StreamWriter(tempHeaderFile);

            sourceFile.WriteLine(
                "#pragma warning( disable: 4995 4996 )\n" +
                "\n" +
                "#include <windows.h>\n" +
                "#include <strsafe.h>\n" +
                "#include <assert.h>\n" +
                "\n" +
                "#include \"BasicDataTypes.h\"\n" +
                "#include \"Collections.h\"\n" +
                "#include \"DirectoriesAndFiles.h\"\n" +
                "#include \"Forms.h\"\n" +
                "\n" + headers);

            string mainAppNameSpaceName = string.Empty;
            foreach( KeyValuePair<string,NameSpace> ns in namespaces)
            {
                ns.Value.WriteToFiles(sourceFile, headerFile, windowsApp);
                if (string.IsNullOrEmpty(mainAppNameSpaceName))
                    mainAppNameSpaceName = ns.Value.ConsoleAppNameSpace;
            }

            if (!string.IsNullOrEmpty(mainAppNameSpaceName))
            {
                if (windowsApp)
                    sourceFile.WriteLine(MainWindow());
                else
                    sourceFile.WriteLine(MainConsole(mainAppNameSpaceName));
            }
            else if ( windowsApp )
            {
                foreach (KeyValuePair<string,NameSpace> kvp in namespaces)
                {
                    string functionHeader = kvp.Value.FunctionHeader;
                    if (functionHeader.Contains("::Form"))
                        sourceFile.WriteLine(FormHandlingCode(functionHeader));
                    break;
                }
            }
            sourceFile.Close();
            headerFile.Close();

            // expand line-feeds to carriage-return + line-feeds
            Console.WriteLine("\nCreating {0}", outFileName);
            File.WriteAllText(outFileName, Globals.FinalProcessingStep( File.ReadAllText(tempSourceFile) ) );
            Console.WriteLine("Creating {0}", HeaderFileName);
            File.WriteAllText(HeaderFileName, Globals.FinalProcessingStep( File.ReadAllText(tempHeaderFile) ) );
            File.Delete(tempSourceFile);
            File.Delete(tempHeaderFile);
        }


        private static string MainWindow()
        {
            string s = "target-platform-not-recognized";
            switch (Globals.TargetPlatform)
            {
                case "win32":
                    s = "\n\n" +
                        "HINSTANCE   g_hInstance = NULL;\n" +
                        "\n" +
                        "Test_002::Form1	Form1;\n" +
                        "\n\n\n\n" +
                        "LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\n" +
                        "{\n" +
                        "\tswitch (message)\n" +
                        "\t{\n" +
                        "\t\tcase WM_CREATE:\n" +
                        "\t\t\tForm1.OnCreate( hWnd );\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_TIMER:\n" +
                        "\t\t\tForm1.OnTimer(wParam,lParam);\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_COMMAND:\n" +
                        "\t\t\tForm1.OnCommand(wParam,lParam);\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_ACTIVATE:\n" +
                        "\t\t\tForm1.OnActivate();\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_SIZE:\n" +
                        "\t\t\tForm1.OnSize();\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_PAINT:\n" +
                        "\t\t\tForm1.OnPaint();\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tcase WM_DESTROY:\n" +
                        "\t\t\tForm1.OnDestroy();\n" +
                        "\t\t\tPostQuitMessage(0);\n" +
                        "\t\t\tbreak;\n" +
                        "\n" +
                        "\t\tdefault:\n" +
                        "\t\t\treturn DefWindowProc(hWnd, message, wParam, lParam);\n" +
                        "\t}\n" +
                        "\treturn 0;\n" +
                        "}\n" +
                        "\n\n" +
                        "BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )\n" +
                        "{\n" +
                        "\tg_hInstance = hInstance; // Store instance handle in our global variable\n" +
                        "\n" +
                        "\tHWND hWnd = CreateWindow( Form1.Text.ToString(), Form1.Text.ToString(), \n" +
                        "\tWS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL );\n" +
                        "\n" +
                        "\tForm1.hWnd = hWnd;\n" +
                        "\tForm1.hInstance = g_hInstance;\n" +
                        "\n" +
                        "\tif (!hWnd)\n" +
                        "\t\treturn FALSE;\n" +
                        "\n" +
                        "\tShowWindow( hWnd, nCmdShow );\n" +
                        "\tUpdateWindow( hWnd );\n" +
                        "\treturn TRUE;\n" +
                        "}\n" +
                        "\n\n" +
                        "ATOM MyRegisterClass(HINSTANCE hInstance)\n" +
                        "{\n" +
                        "\tWNDCLASSEX wcex = {0};\n" +
                        "\n" +
                        "\twcex.cbSize = sizeof(WNDCLASSEX);\n" +
                        "\twcex.style			= CS_HREDRAW | CS_VREDRAW;\n" +
                        "\twcex.lpfnWndProc	= WndProc;\n" +
                        "\twcex.hInstance		= hInstance;\n" +
                        "\twcex.hCursor		= LoadCursor( NULL, IDC_ARROW );\n" +
                        "\twcex.hbrBackground	= (HBRUSH)( COLOR_WINDOW+1 );\n" +
                        "\twcex.lpszClassName  = Form1.Name.ToString();\n" +
                        "\n" +
                        "\treturn RegisterClassEx( &wcex );\n" +
                        "}\n" +
                        "\n" +
                        "int APIENTRY WinMain(   HINSTANCE   hInstance,\n" +
                        "HINSTANCE   hPrevInstance,\n" +
                        "LPSTR       lpCmdLine,\n" +
                        "int         nCmdShow )\n" +
                        "{\n" +
                        "\tUNREFERENCED_PARAMETER( hPrevInstance );\n" +
                        "\tUNREFERENCED_PARAMETER( lpCmdLine );\n" +
                        "\n" +
                        "\tMSG msg;\n" +
                        "\tHACCEL hAccelTable;\n" +
                        "\n" +
                        "\tMyRegisterClass( hInstance );\n" +
                        "\n" +
                        "\tif ( !InitInstance( hInstance, nCmdShow ) )\n" +
                        "\t\treturn FALSE;\n" +
                        "\n" +
                        "\tINITCOMMONCONTROLSEX ic = {0};\n" +
                        "\tic.dwSize = sizeof(ic);\n" +
                        "\tic.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;\n" +
                        "\tInitCommonControlsEx( &ic );\n" +
                        "\n" +
                        "\thAccelTable = Form1.Accelerators(); \n" +
                        "\n" +
                        "\twhile( GetMessage( &msg, NULL, 0, 0 ) )\n" +
                        "\t\tif( !TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) )\n" +
                        "\t\t{\n" +
                        "\t\t\tTranslateMessage( &msg );\n" +
                        "\t\t\tDispatchMessage( &msg );\n" +
                        "\t\t}\n" +
                        "\n" +
                        "\treturn (int) msg.wParam;\n" +
                        "}\n\n\n\n\n";
                    break;
            }
            return s;
        }

        private static string MainConsole(string consoleAppNameSpaceName)
        {
            string s = "target-platform-not-recognized";
            switch (Globals.TargetPlatform)
            {
                case "win32":
                    s = "\n\nint main(int argc, WCHAR* argv[] )\n" +
                        "{\n" +
                        "	List<string> args;\n" +
                        "	// start from '1', .NET skips argv[0]\n" +
                        "	for( int i = 1; i < argc; i++ )\n" +
                        "		args.Add( argv[i] );\n" +
                        "	" + consoleAppNameSpaceName + "::Program::Main( args );\n" +
                        "	return 0;\n" +
                        "}\n";
                    break;
            }
            return s;
        }


        private static string FormHandlingCode(string startWith)
        {
            string s =
                "void " + startWith + "OnCreate( HWND hWnd )\n" +
                "{\n" +
                "\tthis->hWnd = hWnd;\n" +
                "\tSetWindowPos( hWnd, NULL, 0, 0, ClientSize.X + 20, ClientSize.Y + 30, SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER );\n" +
                "\tCreateChildren();\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnSize()\n" +
                "{\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnTimer( WPARAM wParam, LPARAM lParam )\n" +
                "{\n" +
                "\tswitch( wParam )\n" +
                "\t{\n" +
                "\t\tcase ID_FORM1_TIMER_TICK:\n" +
                "\t\t\ttimer_Tick();\n" +
                "\t\t\tbreak;\n" +
                "\t}\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnCommand( WPARAM wParam, LPARAM lParam )\n" +
                "{\n" +
                "\tDebug::WriteLine( string(\"OnCommand: \") + ((int)LOWORD(wParam)) + \"\\n\" );\n" +
                "\tswitch( LOWORD( wParam ) )\n" +
                "\t{\n" +
                "\t\tcase ID_FORM1_BUTTON1_CLICK:\n" +
                "\t\t\tbutton1_Click();\n" +
                "\t\t\tbreak;\n" +
                "\t\tcase ID_FORM1_BUTTON2_CLICK:\n" +
                "\t\t\tbutton2_Click();\n" +
                "\t\t\tbreak;\n" +
                "\t\tcase ID_FORM1_BUTTON3_CLICK:\n" +
                "\t\t\tbutton3_Click();\n" +
                "\t\t\tbreak;\n" +
                "\t}\n" +
                "\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnActivate()\n" +
                "{\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnPaint()\n" +
                "{\n" +
                "\tPAINTSTRUCT	ps;\n" +
                "\tHDC		hdc;\n" +
                "\n" +
                "\thdc = BeginPaint(hWnd, &ps);\n" +
                "\tEndPaint(hWnd, &ps);\n" +
                "}\n" +
                "\n" +
                "void " + startWith + "OnDestroy()\n" +
                "{\n" +
                "}\n" +
                "\n";

            return s;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        private static void ProcessFileHeader(string header)
        {
            Debug.WriteLine(header);
            //Console.WriteLine("file header [" + header + "]\n");
        }
    }

    #endregion

}